package emulator;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.swing.UIManager;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import emulator.GUI.VicUI;
import emulator.assembler.Assembler;
import emulator.cbm.basic.BasicHelper;
import emulator.hardware.bus.Bus;
import emulator.hardware.bus.VicBus;
import emulator.hardware.bus.VideoBus;
import emulator.hardware.clock.Clock;
import emulator.hardware.clock.ClockImpl2;
import emulator.hardware.custom.Fe3Memory;
import emulator.hardware.io.Via6522;
import emulator.hardware.io.peripherals.Joystick;
import emulator.hardware.io.peripherals.KeyboardMatrix;
import emulator.hardware.io.peripherals.SerialPort;
import emulator.hardware.io.peripherals.iec.IecSim;
import emulator.hardware.memory.MemoryBlock;
import emulator.hardware.nmos6502.Cpu6502;
import emulator.hardware.video.Vic6561;
import emulator.shell.DebugShell;
import emulator.shell.GUI.ShellUI;

public class VicMain
{
	static Logger logger = LogManager.getLogger(VicMain.class.getName());
	/**
	 * @param args
	 * @throws EmulatorException 
	 */
	public static void main(String[] args) throws Exception
	{
		logger.entry();
		UIManager.setLookAndFeel(
				UIManager.getSystemLookAndFeelClassName());

		EmulatorConfig config = new EmulatorConfig();
		config.setEnableFe3Support(true);
		config.setEnableIecSimulation(true);
		
		runEmulator(args, config);
	}

	public static void runEmulator(String[] args, EmulatorConfig config) throws IOException, FileNotFoundException
	{
		logger.entry();
		Fe3Memory fe3memory = config.isEnableFe3Support() ? new Fe3Memory() : null;
		MemoryBlock memory = config.isEnableFe3Support() ? fe3memory : new MemoryBlock(65536);
		Bus memory_bus = fe3memory != null ? fe3memory.getMemoryBus() : memory;
		
		memory.loadFromStream(new FileInputStream("data\\characters.901460-03.bin"), 0x8000, 0x1000);
		memory.loadFromStream(new FileInputStream("data\\pal.bin"), 0xC000, 0x4000);
		
		BasicHelper.setMemoryBlock(memory);
		
		Assembler assembler = new Assembler();
		assembler.loadSymbols(new FileInputStream("data\\vicrom.sym"), "System");
		
		Cpu6502 cpu = new Cpu6502();
		Vic6561 vic = new Vic6561();
		Via6522 via1 = new Via6522();
		Via6522 via2 = new Via6522();
		
		VicBus bus = new VicBus(memory_bus, vic, via1, via2);
		if (fe3memory != null)
			bus.setIO3(fe3memory.getIoBus());
		
		Clock clock = new ClockImpl2();
		
		cpu.attach(bus, clock);

		vic.attach(new VideoBus(memory), clock);
		
		via1.attach(clock, cpu.getNmiIn());
		via2.attach(clock, cpu.getIrqIn());
		
		IecSim iec_sim = null;
		if (config.isEnableIecSimulation())
		{
			SerialPort iec_port = new SerialPort();
			iec_port.attachToVia(via2, via1);
			
			iec_sim = new IecSim(cpu);
			iec_sim.attach(clock, iec_port);
		}
		
//		clock.startClock();

		ShellUI ui = new ShellUI("Vic20", cpu, memory, bus, clock);
		ui.setAssembler(assembler);
		ui.setIecSim(iec_sim);
		ui.setVias(via1, via2);
		DebugShell shell = new DebugShell(ui.createIn(), ui.createOut(), ui.createErr());
		shell.init(cpu, bus);
		shell.setMemory(memory_bus);
		shell.setMemoryBlock(memory);
		shell.setAssembler(assembler);
		if (args.length > 0)
			shell.setStartScript(args[0]);
		shell.start();

		KeyboardMatrix matrix = new KeyboardMatrix();
		matrix.attachToVia(via2, via1);
		VicUI.setMatrix(matrix);
		VicUI.setKeyboardMapName("data\\keymap.ini");

		Joystick joystick = new Joystick();
		joystick.attachToVia(via2, via1);
		VicUI.setJoystick(joystick);
		
/*		VicTape tape = new VicTape();
		tape.attachToVia(via2, via1);
		VicUI.setTape(tape);*/
		
		if (config.isEnablePortLogging())
			ui.initPortStateHistory();

		cpu.start();
		vic.start();
		via1.start();
		via2.start();
		if (iec_sim != null)
			iec_sim.start();
		
		VicUI.setClock(clock);
		VicUI.setCpu(cpu);
		VicUI.setAppName("Vic20");
		VicUI.run();
	}
}
